package com.tx.core.util;

import cn.hutool.core.util.StrUtil;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.converters.reflection.ObjectAccessException;
import com.thoughtworks.xstream.converters.reflection.PureJavaReflectionProvider;
import com.thoughtworks.xstream.io.HierarchicalStreamDriver;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.naming.NameCoder;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.Xpp3DomDriver;
import com.thoughtworks.xstream.mapper.Mapper;
import com.thoughtworks.xstream.mapper.MapperWrapper;
import com.thoughtworks.xstream.security.AnyTypePermission;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.lang.reflect.Field;
import java.util.Map;
import java.util.WeakHashMap;

/**
 * xstream工具封装
 * 用以处理xml与bean的转换
 * 
 * @author  PengQingyang
 * @version  [版本号, 2012-10-5]
 * @see  [相关类/方法]
 * @since  [产品/模块版本]
 */
public class XstreamUtils {
    
    /** xstream日志记录器 */
    private static Logger logger = LoggerFactory.getLogger(XstreamUtils.class);
    
    /** Xstream弱引用缓存 */
    private static Map<Class<?>, XStream> xstreamMap = new WeakHashMap<Class<?>, XStream>();
    
    /**
     * 转换过程中特殊字符转码
     */
    private static NameCoder defaultNameCoder = new NameCoder() {
        @Override
        public String encodeNode(String arg0) {
            return arg0;
        }
        
        @Override
        public String encodeAttribute(String arg0) {
            return arg0;
        }
        
        @Override
        public String decodeNode(String arg0) {
            return arg0;
        }
        
        @Override
        public String decodeAttribute(String arg0) {
            return arg0;
        }
    };
    
    /**
      * 增加字符集设置功能
      * <功能详细描述>
      * @param charset
      * @return [参数说明]
      * 
      * @return NameCoder [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    public static NameCoder getCharsetNameCoder(String charset) {
        charset = StrUtil.isEmpty(charset) ? "UTF-8" : charset;
        NameCoder nc = getNameCoder("UTF-8", charset, charset, "UTF-8");
        return nc;
    }
    
    /**
      * 
      * <功能详细描述>
      * @param encodeSourceCharset
      * @param encodeTargetCharset
      * @param decodeSourceCharset
      * @param decodeTargetCharset
      * @return [参数说明]
      * 
      * @return NameCoder [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    public static NameCoder getNameCoder(String encodeSourceCharset,
            String encodeTargetCharset, String decodeSourceCharset,
            String decodeTargetCharset) {
        final String finalEncodeSourceCharset = StrUtil
                .isEmpty(encodeSourceCharset) ? "UTF-8" : encodeSourceCharset;
        final String finalEncodeTargetCharset = StrUtil
                .isEmpty(encodeTargetCharset) ? "UTF-8" : encodeTargetCharset;
        final String finalDecodeSourceCharset = StrUtil
                .isEmpty(decodeSourceCharset) ? "UTF-8" : decodeSourceCharset;
        final String finalDecodeTargetCharset = StrUtil
                .isEmpty(decodeTargetCharset) ? "UTF-8" : decodeTargetCharset;
        NameCoder nameCoder = new NameCoder() {
            @Override
            public String encodeNode(String content) {
                content = transfer(content,
                        finalEncodeSourceCharset,
                        finalEncodeTargetCharset);
                return content;
            }
            
            @Override
            public String encodeAttribute(String content) {
                content = transfer(content,
                        finalEncodeSourceCharset,
                        finalEncodeTargetCharset);
                return content;
            }
            
            @Override
            public String decodeNode(String content) {
                content = transfer(content,
                        finalDecodeSourceCharset,
                        finalDecodeTargetCharset);
                return content;
            }
            
            @Override
            public String decodeAttribute(String content) {
                content = transfer(content,
                        finalDecodeSourceCharset,
                        finalDecodeTargetCharset);
                return content;
            }
            
            private String transfer(String content, String sourceCharset,
                    String targetCharset) {
                if (sourceCharset.equals(targetCharset)) {
                    return content;
                } else {
                    try {
                        content = new String(content.getBytes(sourceCharset),
                                targetCharset);
                    } catch (UnsupportedEncodingException e) {
                        throw new com.tx.core.exceptions.context.UnsupportedEncodingException(
                                MessageUtils.format(
                                        "不支持的字符集.source:{} target:{}",
                                        new Object[] { finalEncodeSourceCharset,
                                                finalEncodeTargetCharset }));
                    }
                }
                return content;
            }
        };
        return nameCoder;
    }
    
    /**
      * 在xml中多余的节点生成bean时会抛出异常
      * 通过该mapperWrapper跳过不存在的属性
      * @param mapper
      * @return [参数说明]
      * 
      * @return MapperWrapper [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    private static MapperWrapper createSkipOverElementMapperWrapper(
            Mapper mapper) {
        MapperWrapper resMapper = new MapperWrapper(mapper) {
            
            /**
             * @param definedIn
             * @param fieldName
             * @return
             */
            @SuppressWarnings("rawtypes")
            @Override
            public boolean shouldSerializeMember(Class definedIn,
                    String fieldName) {
                if (!super.shouldSerializeMember(definedIn, fieldName)) {
                    return false;
                } else {
                    if (FieldUtils.getDeclaredField(definedIn,
                            fieldName,
                            true) == null) {
                        return false;
                    } else {
                        return true;
                    }
                }
            }
        };
        return resMapper;
    }
    
    /**
     * 获取xstream转换对象
     * @param classType
     * @return [参数说明]
     * 
     * @return XStream [返回类型说明]
     * @exception throws [异常类型] [异常说明]
     * @see [类、类#方法、类#成员]
    */
    public static XStream getXstream(Class<?> classType) {
        return getXstream(classType, true, true);
    }
    
    /**
      * 获取Xstream对象<br/>
      * <功能详细描述>
      * @param classType
      * @param isSkipOverElement
      * @param isNewLine
      * @return [参数说明]
      * 
      * @return XStream [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    public static XStream getXstream(Class<?> classType,
            boolean isSkipOverElement, boolean isNewLine) {
        return getXstream(classType,
                defaultNameCoder,
                isSkipOverElement,
                isNewLine);
    }
    
    /**
      * 获取对应字符集的xstream对象
      * <功能详细描述>
      * @param classType
      * @param charset
      * @param isSkipOverElement
      * @param isNewLine
      * @return [参数说明]
      * 
      * @return XStream [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    public static XStream getXstream(Class<?> classType, String charset,
            boolean isSkipOverElement, boolean isNewLine) {
        return getXstream(classType,
                getCharsetNameCoder(charset),
                isSkipOverElement,
                isNewLine);
    }
    
    /**
      * <获取xstream转换对象>
      * <功能详细描述>
      * @param classType
      * @param isSkipOverElement
      * @param isNewLine
      * @return [参数说明]
      * 
      * @return XStream [返回类型说明]
      * @exception throws [异常类型] [异常说明]
      * @see [类、类#方法、类#成员]
     */
    public static XStream getXstream(Class<?> classType, NameCoder nameCoder,
            boolean isSkipOverElement, boolean isNewLine) {
        if (xstreamMap.containsKey(classType)) {
            return xstreamMap.get(classType);
        }
        if (nameCoder == null) {
            nameCoder = defaultNameCoder;
        }
        /**
         * 生成domDriver 重写createWriter方法，使生成的domDriver在新的节点不会新生成一行
         */
        HierarchicalStreamDriver domDriver = null;
        if (isNewLine) {
            domDriver = new Xpp3DomDriver(nameCoder);
        } else {
            domDriver = new Xpp3DomDriver(nameCoder) {
                @Override
                public HierarchicalStreamWriter createWriter(Writer out) {
                    return new PrettyPrintWriter(out, getNameCoder()) {
                        //换行不加换行符
                        @Override
                        protected String getNewLine() {
                            return "";
                        }
                        
                        //行结束时不加多余的空格 
                        @Override
                        protected void endOfLine() {
                            return;
                        }
                    };
                }
            };
        }
        
        XStream res = null;
        if (isSkipOverElement) {
            res = new XStream(new FieldDefaultValueProvider(), domDriver) {
                @Override
                protected MapperWrapper wrapMapper(MapperWrapper next) {
                    return createSkipOverElementMapperWrapper(next);
                }
            };
            //res = new XStream(domDriver);
            res.addPermission(AnyTypePermission.ANY);
        } else {
            res = new XStream(new FieldDefaultValueProvider(), domDriver);
        }
        //XStream res = new XStream(domDriver);
        
        logger.info("create xstream by {0} , parameter {1}",
                new Object[] { classType.getName(), isSkipOverElement });
        
        res.processAnnotations(classType);
        
        xstreamMap.put(classType, res);
        
        return res;
    }
    
    /**
     * 解决反射期间使用类中默认值的问题<br/>
     * https://blog.csdn.net/qq_32331073/article/details/79942472/
     * <功能详细描述>
     * 
     * @author  PengQingyang
     * @version  [版本号, 2022年11月16日]
     * @see  [相关类/方法]
     * @since  [产品/模块版本]
     */
    public static class FieldDefaultValueProvider
            extends PureJavaReflectionProvider {
        /**
         * @param object 目标类的实例
         * @param fieldName XML中显示指明的字段
         * @param definedIn 父类或者类本身
         */
        @Override
        public void writeField(Object object, String fieldName, Object value,
                @SuppressWarnings("rawtypes") Class definedIn) {
            Field field = fieldDictionary
                    .field(object.getClass(), fieldName, definedIn);//返回存在于xml中的字段  
            validateFieldAccess(field);//验证字段可以被访问 
            try {
                if (value instanceof String) {
                    String trim = ((String) value).trim();//字符串首尾去空
                    if (trim.length() == 0)//如果是空字符串，则不做赋值，使用默认初始值
                        return;
                    field.set(object, trim);
                } else {
                    field.set(object, value);
                }
            } catch (IllegalArgumentException e) {
                throw new ObjectAccessException("Could not set field "
                        + object.getClass() + "." + field.getName(), e);
            } catch (IllegalAccessException e) {
                throw new ObjectAccessException("Could not set field "
                        + object.getClass() + "." + field.getName(), e);
            }
        }
    }
    
}
